option(FFT_SINGLE "Use single precision FFTs instead of double precision FFTs" OFF)
set(FFTW "FFTW3")
if(FFT_SINGLE)
  set(FFTW "FFTW3F")
  target_compile_definitions(lammps PRIVATE -DFFT_SINGLE)
endif()
find_package(${FFTW} QUIET)
if(${FFTW}_FOUND)
  set(FFT "FFTW3" CACHE STRING "FFT library for KSPACE package")
else()
  set(FFT "KISS" CACHE STRING "FFT library for KSPACE package")
endif()
set(FFT_VALUES KISS FFTW3 MKL NVPL)
set_property(CACHE FFT PROPERTY STRINGS ${FFT_VALUES})
validate_option(FFT FFT_VALUES)
string(TOUPPER ${FFT} FFT)

if(FFT STREQUAL "FFTW3")
  find_package(${FFTW} REQUIRED)
  target_compile_definitions(lammps PRIVATE -DFFT_FFTW3)
  target_link_libraries(lammps PRIVATE ${FFTW}::${FFTW})
  if(FFTW3_OMP_LIBRARIES OR FFTW3F_OMP_LIBRARIES)
    option(FFT_FFTW_THREADS "Use threaded FFTW library" ON)
  else()
    option(FFT_FFTW_THREADS "Use threaded FFT library" OFF)
  endif()

  if(FFT_FFTW_THREADS)
    if(FFTW3_OMP_LIBRARIES OR FFTW3F_OMP_LIBRARIES)
      target_compile_definitions(lammps PRIVATE -DFFT_FFTW_THREADS)
      target_link_libraries(lammps PRIVATE ${FFTW}::${FFTW}_OMP)
    else()
      message(FATAL_ERROR "Need OpenMP enabled FFTW3 library for FFT_THREADS")
    endif()
  endif()
elseif(FFT STREQUAL "MKL")
  find_package(MKL REQUIRED)
  target_compile_definitions(lammps PRIVATE -DFFT_MKL)
  option(FFT_MKL_THREADS "Use threaded MKL FFT" ON)
  if(FFT_MKL_THREADS)
    target_compile_definitions(lammps PRIVATE -DFFT_MKL_THREADS)
  endif()
  target_link_libraries(lammps PRIVATE MKL::MKL)
elseif(FFT STREQUAL "NVPL")
  find_package(nvpl_fft REQUIRED)
  target_compile_definitions(lammps PRIVATE -DFFT_NVPL)
  target_link_libraries(lammps PRIVATE nvpl::fftw)
else()
  # last option is KISSFFT
  target_compile_definitions(lammps PRIVATE -DFFT_KISS)
endif()

option(FFT_USE_HEFFTE  "Use heFFTe as the distributed FFT engine, overrides the FFT option."  OFF)
if(FFT_USE_HEFFTE)
  # if FFT_HEFFTE is enabled, use the heFFTe parallel engine instead of the builtin fftMPI engine

  # map standard FFT choices to available heFFTe backends: FFTW3 -> FFTW, KISS -> BUILTIN
  set(FFT_HEFFTE_BACKEND_VALUES FFTW MKL BUILTIN)
  string(REPLACE FFTW3 FFTW FFT_HEFFTE_BACKEND_DEFAULT ${FFT})
  string(REPLACE KISS BUILTIN FFT_HEFFTE_BACKEND_DEFAULT ${FFT_HEFFTE_BACKEND_DEFAULT})
  set(FFT_HEFFTE_BACKEND "${FFT_HEFFTE_BACKEND_DEFAULT}" CACHE STRING "Select heFFTe backend, e.g., FFTW or MKL")
  set_property(CACHE FFT_HEFFTE_BACKEND PROPERTY STRINGS ${FFT_HEFFTE_BACKEND_VALUES})
  validate_option(FFT_HEFFTE_BACKEND FFT_HEFFTE_BACKEND_VALUES)

  if(FFT_HEFFTE_BACKEND STREQUAL "FFTW") # respect the backend choice, FFTW or MKL
    set(HEFFTE_COMPONENTS "FFTW")
    set(Heffte_ENABLE_FFTW "ON" CACHE BOOL "Enables FFTW backend for heFFTe")
  elseif(FFT_HEFFTE_BACKEND STREQUAL "MKL")
    set(HEFFTE_COMPONENTS "MKL")
    set(Heffte_ENABLE_MKL "ON" CACHE BOOL "Enables MKL backend for heFFTe")
  else()
    set(HEFFTE_COMPONENTS "BUILTIN")
    message(WARNING "FFT_HEFFTE_BACKEND not selected, defaulting to the builtin 'stock' backend, which is intended for testing and is not optimized for production runs")
  endif()

  find_package(Heffte 2.4.0 QUIET COMPONENTS ${HEFFTE_COMPONENTS})
  if (NOT Heffte_FOUND) # download and build
    if(BUILD_SHARED_LIBS)
      set(BUILD_SHARED_LIBS_WAS_ON YES)
      set(BUILD_SHARED_LIBS OFF)
    endif()
    if(CMAKE_REQUEST_PIC)
      set(CMAKE_POSITION_INDEPENDENT_CODE ON)
    endif()
    set(Heffte_ENABLE_${FFT_HEFFTE_BACKEND} ON)
    include(FetchContent)
    FetchContent_Declare(HEFFTE_PROJECT # using v2.4.0
      URL  "https://github.com/icl-utk-edu/heffte/archive/refs/tags/v2.4.0.tar.gz"
      URL_HASH SHA256=02310fb4f9688df02f7181667e61c3adb7e38baf79611d80919d47452ff7881d
      )
    FetchContent_Populate(HEFFTE_PROJECT)

    # fixup git hash to show "(unknown)" to avoid compilation failures.
    file(READ ${heffte_project_SOURCE_DIR}/include/heffte_config.cmake.h HEFFTE_CFG_FILE_TEXT)
    string(REPLACE "@Heffte_GIT_HASH@" "(unknown)" HEFFTE_CFG_FILE_TEXT "${HEFFTE_CFG_FILE_TEXT}")
    file(WRITE ${heffte_project_SOURCE_DIR}/include/heffte_config.cmake.h "${HEFFTE_CFG_FILE_TEXT}")

    add_subdirectory(${heffte_project_SOURCE_DIR} ${heffte_project_BINARY_DIR} EXCLUDE_FROM_ALL)
    add_library(Heffte::Heffte ALIAS Heffte)
    if(BUILD_SHARED_LIBS_WAS_ON)
      set(BUILD_SHARED_LIBS ON)
    endif()
  endif()
  target_compile_definitions(lammps PRIVATE -DFFT_HEFFTE "-DFFT_HEFFTE_${FFT_HEFFTE_BACKEND}")
  target_link_libraries(lammps PRIVATE Heffte::Heffte)
endif()

set(FFT_PACK "array" CACHE STRING "Optimization for FFT")
set(FFT_PACK_VALUES array pointer memcpy)
set_property(CACHE FFT_PACK PROPERTY STRINGS ${FFT_PACK_VALUES})
validate_option(FFT_PACK FFT_PACK_VALUES)
if(NOT FFT_PACK STREQUAL "array")
  string(TOUPPER ${FFT_PACK} FFT_PACK)
  target_compile_definitions(lammps PRIVATE -DFFT_PACK_${FFT_PACK})
endif()
